package testrank;

import java.io.File;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.Map.Entry;

import org.aspectj.lang.reflect.SourceLocation;

/**
 * @author Hagai Cibulski
 */
public class SourceElementLocator implements Serializable {

	private static final long serialVersionUID = 1L;
	
	/**
	 * fileName -> beginLine -> ElementInfo
	 */
	private Map<String, SortedMap<Integer, ElementInfo>> locations = new HashMap<String, SortedMap<Integer,ElementInfo>>();

	private Map<String, String> pathNameByFileName = new HashMap<String, String>();
	
	private Map<String, ElementInfo> codeElementsBySignature = new HashMap<String, ElementInfo>(); 

	public void begin(String methodSignature, String fileName, int line) {
		
		SortedMap<Integer, ElementInfo> methodByLine= locations.get(fileName);
		if (methodByLine == null) { 
			methodByLine = new TreeMap<Integer, ElementInfo>();
			locations.put(fileName, methodByLine);
		}
		if ( ! methodByLine.containsKey(line)) {
			ElementInfo codeElem = new ElementInfo(methodSignature, line, -1);
			methodByLine.put(line, codeElem);
			codeElementsBySignature.put(methodSignature, codeElem);
			if (methodSignature.equals("double[] org.apache.commons.math.optimization.general.NonLinearConjugateGradientOptimizerTest.LinearProblem.gradient(double[])")){
				System.out.println("DEBUGGING");
			}
		}
	}
	
	public void end(String methodSignature, String fileName, int beginLine, int returnLine) {
		SortedMap<Integer, ElementInfo> methodByLine= locations.get(fileName);
		if (methodByLine == null) { 
			throw new IllegalStateException("end without begin: "+methodSignature+"@"+fileName+":"+beginLine);
		}
		ElementInfo elementInfo = methodByLine.get(beginLine);
		if (elementInfo.getEndLocation() == -1) {
			elementInfo.setEndLocation(returnLine);
		}
	}
	
	public boolean hasStart(String methodSignature) {
		return codeElementsBySignature.containsKey(methodSignature);
	}
	
	public boolean hasEnd(String methodSignature) {
		if (!hasStart(methodSignature)) {
			return false; //TODO shouldn't happen but does e.g. for Singleton constructor 
		}
		return codeElementsBySignature.get(methodSignature).getEndLocation() != -1;
	}
	
	/**
	 * @return signature of the method containing the location given by fileName:line,
	 * 			or null if not found
	 */
	public String locateMethod(String fileName, int line) {
		SortedMap<Integer, ElementInfo> methodByLine= locations.get(fileName);
		if (methodByLine != null) {
			for (ElementInfo elem : methodByLine.values()) {
				if (line >= elem.getBeginLocation() && line <= elem.getEndLocation()) {
					return elem.getSignature();
				}
			}
		}
		return null;
	}

	@Override
	public String toString() {
		return locations.toString().replace("}, ", "},\n");
	}
	
	public Set<Entry<String, SortedMap<Integer, ElementInfo>>> getAllLocations() {
		return locations.entrySet();
	}

	public void putMethodLocation(SourceLocation methodLocation, String baseDir) {
		String fileName = methodLocation.getFileName();
		String packageName = methodLocation.getWithinType().getPackage().getName();
		String filePath = baseDir + File.separatorChar + packageName.replace('.', File.separatorChar) + File.separatorChar + fileName;
		pathNameByFileName.put(fileName, filePath);
	}

	public String getFilePath(String fileName) {
		return pathNameByFileName.get(fileName);
	}
	
	public ElementInfo getCodeElementBySignature(String signature) {
		return codeElementsBySignature.get(signature);
	}
}
